#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include<string> 

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

//2、命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;

//2、环境变量
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;


//3、重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
int redir = NONE_REDIR;
std::string filename;



// for test
char cwd[1024];
char cwdenv[1024];

// last exit code
int lastcode = 0;

const char* GetUserName()
{
   const char* name = getenv("USER");
   return name == NULL ? "None" : name;
}

const char* GetHostName()
{
    const char* hostname = getenv("HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

const char* GetPwd()
{
  // const char* pwd = getenv("PWD");
  const char* pwd = getcwd(cwd, sizeof(cwd));
  if(pwd != NULL)
  {
     snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
     putenv(cwdenv);
  }
   return pwd == NULL ? "None" : pwd;
}

const char* GetHome()
{
  const char* home = getenv("HOME");
  return home == NULL ? "" : home;
}


void InitEnv()
{
  extern char** environ;
  memset(g_env, 0, sizeof(g_env));
  g_envs = 0;
  
  //1、获取环境变量：从父shell
  for(int i = 0; environ[i]; i++)
  {
     //申请空间
     g_env[i] = (char*)malloc(strlen(environ[i]) + 1);
     //拷贝数据
     strcpy(g_env[i], environ[i]);
     g_envs++;
  }

  g_env[g_envs++] = (char*) "lllllll"; //用于测试
  g_env[g_envs] = NULL;

  //2、导出环境变量
  for(int i = 0; g_env[i]; i++)
  {
    putenv(g_env[i]);
  }
  environ = g_env;
}


bool Cd()
{
  if(g_argc == 1)
  {
    std:: string home = GetHome();
    if(home.empty()) return true;
    chdir(home.c_str());//切换到家目录
  }
  else
  {
   std::string where = g_argv[1];
        // cd - / cd ~
        if(where == "-")
        {
            // Todu
        }
        else if(where == "~")
        {
            std:: string home = GetHome();
            if(home.empty()) return true;
            chdir(home.c_str());//切换到家目录    
        }
        else
        {
            chdir(where.c_str());
        }
  }
  return true;
}

void Echo()
{
  if(g_argc == 2)
  {
    // echo "hello world"
        // echo $?
        // echo $PATH
        std::string opt = g_argv[1];
        if(opt == "$?")
        {
            std::cout << lastcode << std::endl;
            lastcode = 0;
        }
        else if(opt[0] == '$')
        {
            std::string env_name = opt.substr(1);
            const char *env_value = getenv(env_name.c_str());
            if(env_value)
                std::cout << env_value << std::endl;
        }
        else
        {
            std::cout << opt << std::endl;
        }
  }
}

std::string DirName(const char* pwd)
{
  #define SLASH "/"
  std::string dir = pwd;
  if(dir == SLASH) return SLASH;
  auto pos = dir.rfind(SLASH);
  if(pos == std::string::npos) return "BUG?";
  return dir.substr(pos+1);
}


void MakeCommandLine(char cmd_prompt[], int size)
{
  // snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), GetPwd());
  snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}


bool GetCommandLine(char* out, int size)
{
  char* c = fgets(out, size, stdin);
  if(c == NULL) return 1;
  out[strlen(out)-1] = 0;//清理\n
  if(strlen(out) == 0) return false;
  return true;
}

void PrintCommandPrompt()
{
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt, sizeof(prompt));
  printf("%s", prompt);
  fflush(stdout);
}

//3、命令行参数分析 "ls -a -l"
bool CommandParse(char* commandline)
{
   #define SEP " "
   g_argc = 0;
    // 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
    g_argv[g_argc++] = strtok(commandline, SEP);
    while((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));
    g_argc--;
    return g_argc > 0 ? true:false;
}

void PrintArgv()
{
  for(int i = 0; g_argv[i]; i++)
  {
     printf("argv[%d]->%s\n", i, g_argv[i]);
  }
  printf("argc: %d\n", g_argc);
}

int Execute()
{
   pid_t id = fork();
   if(id == 0)
   {
      //child
      execvp(g_argv[0], g_argv);
      exit(1);
   }

   int status = 0;
   // father
   pid_t rid = waitpid(id, &status, 0);
   if(rid > 0)
   {
      lastcode = WEXITSTATUS(status);
   }
   return 0;
}

bool CheckAndExecBuiltin()
{
  std::string cmd = g_argv[0];
  if(cmd == "cd")
  {
     Cd();
     return true;
  }
  else if(cmd == "echo")
  {
     Echo();
     return true;
  }
  else if(cmd == "alias")
  {

     return true;
  }

  return false;
}

void TrimSpace(char cmd[], int end)
{
  while(isspace(cmd[end]))
    end++;
}



void RedirCheck(char cmd[])
{
   redir = NONE_REDIR;
   filename.clear();
   int start = 0;
   int end =strlen(cmd) - 1;

   //"ls -a -l >> file.txt" > >> <
   while(end > start)
   {
     if(cmd[end] == '<')
     {
       cmd[end++] = 0;
       //清掉空格
       TrimSpace(cmd, end);
       redir = INPUT_REDIR;
       filename = cmd + end;
       break;
     }
     else if(cmd[end] == '>')
     {
       if(cmd[end - 1] == '>')
       {
         //>>
         cmd[end-1] = 0;
         redir = APPEND_REDIR;
       }
       else
       {
         //>
         redir = OUTPUT_REDIR;
       }
       cmd[end++] = 0;
       TrimSpace(cmd, end);
       filename = cmd + end;
       break;
     }
     else
     {
        end--;
     }
   }
}



int main()
{
  //printf("[%s@%s %s]# ", GetUserName(), GetHostName(), GetPwd());
  
  //shell启动的时候，从系统中获取环境变量
  //我们的环境变量信息应该从父shell统一来
  InitEnv();

  while(true)
  {

    //1、输出命令行提示符
    PrintCommandPrompt();
    //2、获取用户输入的命令
    char commandline[COMMAND_SIZE];
    if(!GetCommandLine(commandline, sizeof(commandline)))
    {    
     // printf("echo %s\n", commandline);
       continue;
    }

    //3、重定向分析
    RedirCheck(commandline);

    //4、命令行分析
    if(!CommandParse(commandline))
      continue;
     // PrintArgv();

    //5、检测并处理内建命令
    if(CheckAndExecBuiltin())
       continue;
    
    //6、执行命令
    Execute();
  }

  return 0;
}
